#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/blkdev.h>

/*块设备驱动模版*/
/*
  1.块设备的IO操作和字符设备的IO操作差异点
  2.块设备驱动的结构，主要是数据结构，函数及其关系
  3.加载、卸载、打开和释放以及ioctl操作（ioctl函数可以像块设备发送命令，通常用于执行那些不能通过标准读写操作完成的任务）
  4.块设备IO操作所依赖的请求，以及bio操作（****）
       请求队列在块设备驱动中很重要：像对块设备的读写操作，都是通过请求队列来完成的；读取一个扇区的某一些地址的时候，需要先通过请求队列来找到对应的扇区，然后进行读取操作

*/

/*
  1.块设备对于IO请求有对应的缓冲；可以调节读写的顺序
  2.块设备可以被随机访问
*/

#define BLK_MAJOR 0
#define BLK_MINOR 0
#define DISK_NAME_LEN 32
#define BLK_DEV_NAME "blockDev"

/* file_operations 用于字符驱动设备的结构体 */
// static struct file_operations blockDev_fops = {
//     .owner = THIS_MODULE,
//     .open = blockDev_open,
//     .release = blockDev_release,
//     .read = blockDev_read,
//     .write = blockDev_write,

// };

/* block_device_operator 用于块设备驱动 */
struct block_device_operations blockDev_fops = {
    .owner = THIS_MODULE,    /* 模块指针 */
    .open = blockDev_open,
    .release = blockDev_release,
    .read = blockDev_read,
    .write = blockDev_write,
    /* IO控制操作 */
    .ioctl = blockDev_ioctl,
    .compat_ioctl = blockDev_compat_ioctl,
    /*介质改变：检查驱动器中的介质是否改变；通常用于检查可移动设备*/
    .media_change = blockDev_media_change,  //已经被 check_event 替代
    /*检查事件：检查驱动器中的介质是否改变；通常用于检查可移动设备*/
    .check_events = blockDev_check_events,
    /*获取驱动器信息*/
    .getgeo = blockDev_getgeo,
    
};

/* 表示一个磁盘设备 ；跟使用 gendisk 和字符设备的 cdev 类似  */
struct gendisk {
    int major;  /* 主设备号 */
    int minor;  /* 次设备号 */
    char disk_name[DISK_NAME_LEN];  /* 磁盘名称 */
    struct block_device_operations fops;  /* 块设备操作函数 */
    struct request_queue *queue;  /* 请求队列 */
    struct list_head queue_node;  /* 队列节点 */
    struct list_head part_node;  /* 分区节点 */
    struct hd_struct part[0];  /* 分区结构体数组 */
    unsigned int events;        /* 事件标志 */
    unsigned int capacity;       /* 磁盘容量 */
    // ... ...
};

/* bio : 通常对应上层传递给快层的请求 每个 bio 实例及其包含的 bvec_iter 和 bio_vec 结构体 描述了这个IO请求的开始扇区，读写方向，数据放入页 */
struct bvec_iter {
    unsigned int bi_sector;  /* 请求开始扇区 */
    unsigned int bi_size;  /* 请求大小 */
    unsigned int bi_offset;  /* 请求偏移 */
    unsigned int bi_bvec_done;  /* 已完成的 bio_vec 数量 */
    unsigned int bi_idx;  /* 当前 bio_vec 的索引 */
    unsigned int bi_size_done;  /* 已完成的请求大小 */
}

struct bio_vec {
    struct page *bv_page;  /* 页指针 */
    unsigned int bv_len;  /* 页长度 */
    unsigned int bv_offset;  /* 页偏移 */
    unsigned int bv_gap;  /* 页间隙 */
    // ... ...
}


struct bio {
    struct bio *bi_next;  /* 下一个 bio */
    struct bvec_iter bi_iter;  /* bio 迭代器 */
    struct bio_vec *bi_io_vec;  /* bio_vec 数组 */
}
/* 一些 bio 处理的主要 API ；通过请求队列，对BIO实例中的一些地址进行读写操作 */
// request_queue   * blk_init_queue(request_fn_proc *rfn, spinlock_t *lock);  // 初始化请求队列
// void blk_cleanup_queue(request_queue *q);    // 清理请求队列
// request_queue * blk_alloc_queue(int flags);  // 分配请求队列
// void blk_queue_start(request_queue *q);   // 启动请求队列
// struct request *blk_fetch_request(request_queue *q, int *requeue);  // 从请求队列中获取请求

static struct request_queue * blk_dev_queue = NULL;  // 块设备请求队列
struct gendisk *blk_dev_disk = NULL;

static int blockDev_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "blockDev_open\n");
    return 0;
}

static int blockDev_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "blockDev_release\n");
    return 0;
}

static ssize_t blockDev_read(struct file *file, char __user *buf, size_t len, loff_t *off)
{
    printk(KERN_INFO "blockDev_read\n");
    return 0;
}

static ssize_t blockDev_write(struct file *file, const char __user *buf, size_t len, loff_t *off)
{
    
    printk(KERN_INFO "blockDev_write\n");
    return 0;
}


static void queue_req_callback(struct request_queue *req)
{
    printk(KERN_INFO "queue_req_callback\n");
    /* 当请求队列中有请求时，可以通过该回调进行处理 */
}

static int __init blockDev_init(void)
{
    printk(KERN_INFO "blockDev_init\n");
    /* 注册块设备驱动 */
    if(register_blkdev(BLK_MAJOR, BLK_DEV_NAME) < 0)
    {
        printk(KERN_INFO "register_blkdev failed\n");
        return -1;
    }
    /* 创建块请求队列 （通过第一个形参，可以绑定一个请求函数，当请求队列中有请求的时候，就会执行绑定的函数）*/
    blk_dev_queue = blk_init_queue(queue_req_callback, NULL);
    if(!blk_dev_queue)
    {
        printk(KERN_INFO "blk_init_queue failed\n");
        return -1;
    }
    /* 设置请求队列的最大扇区；主要用于通知 i/o 调度器   */
    blk_queue_max_hw_sectors(blk_dev_queue, 256);
    /* 设置块设备请求队列的逻辑块大小 */
    blk_queue_max_logical_block_size(blk_dev_queue, 512);

    /* gendisk 初始化 */
    blk_dev_disk = alloc_disk(1);
    if(!blk_dev_disk)
    {
        printk(KERN_INFO "alloc_disk failed\n");
        return -1;
    }
    blk_dev_disk->major = BLK_MAJOR;
    blk_dev_disk->first_minor = 0;
    blk_dev_disk->fops = &blk_dev_fops;
    blk_dev_disk->queue = blk_dev_queue;
    strcpy(blk_dev_disk->disk_name, BLK_DEV_NAME);
    set_capacity(blk_dev_disk, 1024);
    add_disk(blk_dev_disk);

    return 0;

}

static void __exit blockDev_exit(void)
{

    printk(KERN_INFO "blockDev_exit\n");
}



module_init(blockDev_init);
module_exit(blockDev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangsan");





/*
 块设备驱动中，可以使用请求队列来管理IO请求，也可以不使用请求队列，直接使用 bio 结构体来处理IO请求
    使用请求队列  ：可以有效的处理大量的IO请求，并且可以有效的管理IO请求的优先级，同时提供一种标准的方式来处理IO请求
    不使用请求队列：就需要自己来对这些IO请求进行处理，包括请求的调度，请求的完成，请求的错误处理等等，相对来说比较复杂
*/

/*例子 ： */



static struct request_queue *blk_dev_queue = NULL;
static struct gendisk *blk_dev_disk = NULL;

static void my_request_func(struct request_queue *q)
{
    struct request *req;
    /* 从请求队列中取出请求，进行处理 */
    while((req = blk_fetch_request(q)) != NULL)
    {
        blk_end_request_all(req, 0);
    }
}


static int __init blockDev_init(void)
{
    my_queue = blk_init_queue(my_request_func, NULL);
    if(!my_queue)
    {
        return -ENOMEM;
    }
    my_disk = alloc_disk(1 << BLK_MINOR);
    if(!my_disk)
    {
        blk_cleanup_queue(my_queue);
        return -ENOMEM;
    }
    my_disk->major = BLK_MAJOR;
    my_disk->first_minor = BLK_MINOR;
    my_disk->fops = &blk_dev_fops;
    my_disk->queue = my_queue;      // 将请求队列绑定到块设备上
    strcpy(my_disk->disk_name, BLK_DEV_NAME);
    set_capacity(my_disk, 1024);
    add_disk(my_disk);
    return 0;
}


static void __exit blockDev_exit(void)
{
    remove_disk(my_disk);
    put_disk(my_disk);
    blk_cleanup_queue(my_queue);

}

module_init(blockDev_init);
module_exit(blockDev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zhangsan");


/*
  不使用请求队列的： 
*/

#define MY_MAJOR 240
#define MY_MINOR 0
#define MY_DEVICE_NAME "my_block_device"
#define MY_DEVICE_SIZE (4 * 1024 * 1024) // 4MB


static char *my_device_memory;
static struct class *my_class;
static struct device *my_device;

static int my_open(struct inode *inode, struct file *filp)
{
    return 0;
}

static int my_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static ssize_t my_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    // 处理读请求
    return count;
}

static ssize_t my_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos)
{
    // 处理写请求
    return count;
}

static struct file_operations my_block_device_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_release,
    .read = my_read,
    .write = my_write,
};

static int __init my_block_device_init(void)
{
    int result;

    result = register_chrdev(MY_MAJOR, MY_DEVICE_NAME, &my_block_device_fops);
    if (result < 0) {
        printk(KERN_WARNING "my_block_device: can't get major number %d\n", MY_MAJOR);
        return result;
    }
    my_device_memory = kmalloc(MY_DEVICE_SIZE, GFP_KERNEL);
    if (!my_device_memory) {
        unregister_chrdev(MY_MAJOR, MY_DEVICE_NAME);
        return -ENOMEM;
    }
    my_class = class_create(THIS_MODULE, MY_DEVICE_NAME);
    if (IS_ERR(my_class)) {
        kfree(my_device_memory);
        unregister_chrdev(MY_MAJOR, MY_DEVICE_NAME);
        return PTR_ERR(my_class);
    }
    my_device = device_create(my_class, NULL, MKDEV(MY_MAJOR, MY_MINOR), NULL, MY_DEVICE_NAME);
    if (IS_ERR(my_device)) {
        class_destroy(my_class);
        kfree(my_device_memory);
        unregister_chrdev(MY_MAJOR, MY_DEVICE_NAME);
        return PTR_ERR(my_device);
    }

    return 0;
}

static void __exit my_block_device_exit(void)
{
    device_destroy(my_class, MKDEV(MY_MAJOR, MY_MINOR));
    class_destroy(my_class);
    kfree(my_device_memory);
    unregister_chrdev(MY_MAJOR, MY_DEVICE_NAME);

}

module_init(my_block_device_init);
module_exit(my_block_device_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("SJZ");




/*
   VMEM 块设备的初始化代码：  
   分配空间给私有数据(kzalloc) ==> 初始化块设备请求队列(blk_queue_init) ==> 初始化块设备(alloc_disk) ==> 绑定块设备的设备号、名称、操作函数、私有数据、请求队列等
   设置块设备容量(set_capacity) ==> 注册块设备(add_disk) ==> 注册块设备(register_blkdev)
*/

#define VMDISK_MAJOR 240
#define VMDISK_MINOR 0
#define VMDISK_DEVICE_NAME "vmem_disk"
#define VMDISK_SIZE (16 * 1024 * 1024) // 16MB
#define VMDISK_SECTOR_SIZE 512

static struct gendisk *vmem_disk;
static struct request_queue *vmem_disk_queue;
static char *vmem_disk_memory;


static int vmem_disk_open(struct block_device *bdev, fmode_t mode)
{
    return 0;
}

static void vmem_disk_release(struct gendisk *disk, fmode_t mode)
{
}

static int vmem_disk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
    geo->heads = 1;
    geo->cylinders = VMDISK_SIZE / (16 * 63);
    geo->sectors = 16;
    geo->start = 0;
    return 0;
}
/* 对块设备的操作封装 */
static struct block_device_operations vmem_disk_ops = {
    .owner = THIS_MODULE,
    .open = vmem_disk_open,
    .release = vmem_disk_release,
    .getgeo = vmem_disk_getgeo,
};

static void vmem_disk_queue_irq(struct request_queue *q)
{
    // 处理请求队列中的请求
    struct request *req;
    /* 获取请求队列中的请求，并进行处理 */
    while(req = blk_fetch_request(q, 0)) 
    {
        if(req->cmd_type == REQ_TYPE_FS)
        {
            blk_end_request_all(req, 0);
            continue;
        }
        switch (req_data_dir(req))
        {
        case READ:
                memcpy(req->buffer, vmem_disk_memory + blk_rq_pos(req) * VMDISK_SECTOR_SIZE, blk_rq_sectors(req));
            break;
        case WRITE:
                memcpy(vmem_disk_memory + blk_rq_pos(req) * VMDISK_SECTOR_SIZE, req->buffer, blk_rq_sectors(req));
            break;
        default:
            blk_end_request_all(req, -EIO);
            break;
        }
        blk_end_request_all(req, 0);
    }
}

static int __init vmem_disk_init(void)
{
    int result = 0;
    /* 对私有数据分配空间内存 */
    vmem_disk_memory = kzalloc(VMDISK_SIZE, GFP_KERNEL);
    if(devices == NULL)
    {
        return -ENOMEM;
    }
    /* 初始化请求队列 并绑定请求IO的回调函数 */
    vmem_disk_queue = blk_init_queue(vmem_disk_queue_irq, THIS_MODULE);
    if (!vmem_disk_queue) {
        kfree(vmem_disk_memory);
        return -ENOMEM;
    }
    /* 初始化gendisk结构体 ；用于注册块设备 */
    vmem_disk = alloc_disk(1);
    if (!vmem_disk) {
        blk_cleanup_queue(vmem_disk_queue);
        kfree(vmem_disk_memory);
        return -ENOMEM;
    }
    /* 绑定块设备的设备号、操作封装、请求队列、设备名、并将设备注册到内核中 */
    vmem_disk->major = VMDISK_MAJOR;
    vmem_disk->first_minor = VMDISK_MINOR;
    vmem_disk->fops = &vmem_disk_ops;     /* 绑定块设备的读写操作接口封装 */
    vmem_disk->queue = vmem_disk_queue;   /* 绑定请求队列 */
    vmem_disk->private_data = vmem_disk_memory;  /* 绑定私有数据 */
    strcpy(vmem_disk->disk_name, VMDISK_DEVICE_NAME);
    set_capacity(vmem_disk, VMDISK_SIZE / VMDISK_SECTOR_SIZE);

    result = add_disk(vmem_disk);
    if (result) {
        put_disk(vmem_disk);
        blk_cleanup_queue(vmem_disk_queue);
        kfree(vmem_disk_memory);
        return result;
    }
    result = register_chrdev(VMDISK_MAJOR, VMDISK_DEVICE_NAME, &vmem_disk_ops);
    if (result < 0) {
        del_gendisk(vmem_disk);
        put_disk(vmem_disk);
        blk_cleanup_queue(vmem_disk_queue);
        kfree(vmem_disk_memory);
        return result;
    }
    return 0;
}


static void __exit vmem_disk_exit(void)
{
    unregister_chrdev(VMDISK_MAJOR, VMDISK_DEVICE_NAME);
    del_gendisk(vmem_disk);
    put_disk(vmem_disk);
    blk_cleanup_queue(vmem_disk_queue);
    kfree(vmem_disk_memory);
}

module_init(vmem_disk_init);
module_exit(vmem_disk_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("SJZ");



/* 
块设备的驱动和字符设备的还是有很大的不同，主要就是在“请求”上， 通过请求队列，对块设备的 I/O 进行操作，而请求队列中的 I/O 会通过IO调度器进行排队和整合 ；
同时在一些必要场合，也可以认为的制造请求，然后在请求队列的回调中对这个请求进行处理；

块设备的操作主要就是：
1. 请求队列 blk_init_queue  blk_cleanup_queue  blk_fetch_request  blk_end_request_all  blk_rq_pos  blk_rq_sectors  blk_rq_data_dir 
2. 块设备操作封装 struct block_device_operations (open release read write ...)
3. 块设备注册 alloc_disk  add_disk del_gendisk  put_disk   set_capacity
4. 块设备号 register_chrdev  unregister_chrdev
5. 私有数据分配 kcalloc  kfree
6. 请求队列回调函数 vmem_disk_queue_irq

对应的结构体主要有：
1. 请求队列 struct request_queue
2. 块设备操作封装 struct block_device_operations
3. 块设备 struct gendisk
4. 请求队列的请求 struct request

*/